//
//  PIAudioPlay.m
//  playin
//
//  Created by lijunge on 2019/9/6.
//  Copyright © 2019 lijunge. All rights reserved.
//

#import "PIAudioPlay.h"
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>
#define MIN_SIZE_PER_FRAME 2000
#define QUEUE_BUFFER_SIZE 3

@interface PIAudioPlay () {
    
    dispatch_queue_t audioPlayQueue;
    AudioQueueRef audioQueue;                                 //音频播放队列
    AudioStreamBasicDescription _audioDescription;
    AudioQueueBufferRef audioQueueBuffers[QUEUE_BUFFER_SIZE]; //音频缓存
    BOOL audioQueueBufferUsed[QUEUE_BUFFER_SIZE];             //判断音频缓存是否在使用
    NSLock *lock;
    OSStatus osState;
}


@property (nonatomic, assign) int sampleRate;
@property (nonatomic, assign) int bitChannel;
@property (nonatomic, assign) int channelFrame;
@property (nonatomic, assign) BOOL isPause;
@end

@implementation PIAudioPlay

- (instancetype)init {
    
    if (self = [super init]) {
        [self addObservers];
    }
    return self;
}

- (void)configAudioDescriptionWithSampleRate:(int)sampleRate
                              bitsPerChannel:(int)bitChannel
                                channelFrame:(int)channelFrame {
    
    lock = [NSLock new];
    //设置音频参数
    self.sampleRate = sampleRate;
    self.bitChannel = bitChannel;
    self.channelFrame = channelFrame;
    _audioDescription.mSampleRate = sampleRate;//采样率
    _audioDescription.mFormatID = kAudioFormatLinearPCM;
    // 下面这个是保存音频数据的方式的说明，如可以根据大端字节序或小端字节序，浮点数或整数以及不同体位去保存数据
    _audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    //1单声道 2双声道
    _audioDescription.mChannelsPerFrame = channelFrame;
    //每一个packet一侦数据,每个数据包下的桢数，即每个数据包里面有多少桢
    _audioDescription.mFramesPerPacket = 1;
    //每个采样点16bit量化 语音每采样点占用位数
    _audioDescription.mBitsPerChannel = bitChannel;
    _audioDescription.mBytesPerFrame = (_audioDescription.mBitsPerChannel / 8) * _audioDescription.mChannelsPerFrame;
    //每个数据包的bytes总数，每桢的bytes数*每个数据包的桢数
    _audioDescription.mBytesPerPacket = _audioDescription.mBytesPerFrame * _audioDescription.mFramesPerPacket;
    AudioQueueNewOutput(&_audioDescription, AudioPlayerAQInputCallback, (__bridge void * _Nullable)(self), nil, 0, 0, &audioQueue);
    // 设置音量
    AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1.0);
    // 初始化需要的缓冲区
    for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
        audioQueueBufferUsed[i] = false;
        osState = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);
        if (osState != noErr) {
            NSLog(@"[PIAudioPlay] AudioQueueAllocateBuffer Error%d",(int)osState);
        }
    }
    [lock unlock];
}

- (void)turnOffAudioPlay:(BOOL)off {
    
    self.volumeOff = off;
    if (off) {
        if (audioQueue != nil) {
            AudioQueueStop(audioQueue,YES);
            AudioQueueReset(audioQueue);
            AudioQueueFlush(audioQueue);
        }
        audioQueue = nil;
    }
}

- (void)stopAudioPlay {
   
    self.isPause = YES;
    if (audioQueue != nil) {
        AudioQueueStop(audioQueue,YES);
        AudioQueueReset(audioQueue);
        AudioQueueFlush(audioQueue);
    }
    audioQueue = nil;
    lock = nil;
}

- (void)playWith:(NSData *)audioData {

    if (self.isPause) {return;}
    if (self.volumeOff) {return;}
    
    if (audioQueue == nil || ![self checkBufferHasUsed]) {

        [self configAudioDescriptionWithSampleRate:self.sampleRate
                                    bitsPerChannel:self.bitChannel
                                      channelFrame:self.channelFrame];
        
        osState = AudioQueueStart(audioQueue, NULL);
        if(osState != noErr) {
            NSLog(@"[PIAudioPlay] AudioQueueStart Error:%d",(int)osState);
            return;
        }
    }

    [lock lock];
    AudioQueueBufferRef audioQueueBuffer = NULL;
    //循环50次寻找可用buffer 50毫秒一个包 循环50 如果超过250毫秒没有找到可用buffer 则丢掉包
    for (int i = 0; i < 50; i++) {
        [NSThread sleepForTimeInterval:0.0005];
        audioQueueBuffer = [self getNotUsedBuffer];
        if (audioQueueBuffer != NULL) {
            break;
        }
    }
    if (audioQueueBuffer == NULL) {
        [lock unlock];
        return;
    }
    audioQueueBuffer -> mAudioDataByteSize = (unsigned int)audioData.length;
    memcpy(audioQueueBuffer -> mAudioData, audioData.bytes, audioData.length);
    AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffer, 0, NULL);
    [lock unlock];
}


- (AudioQueueBufferRef)getNotUsedBuffer {
    
    for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
        if (audioQueueBufferUsed[i] == NO) {
            audioQueueBufferUsed[i] = YES;
            return audioQueueBuffers[i];
        }
    }
    return NULL;
}

- (BOOL)checkBufferHasUsed {

    for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
        if (audioQueueBufferUsed[i] == YES) {
            return YES;
        }
    }
    return NO;
}

static void AudioPlayerAQInputCallback(void* inUserData,AudioQueueRef audioQueueRef, AudioQueueBufferRef audioQueueBufferRef) {
    
    PIAudioPlay *player = (__bridge PIAudioPlay*)inUserData;
    [player resetBufferState:audioQueueRef and:audioQueueBufferRef];
}

- (void)resetBufferState:(AudioQueueRef)audioQueueRef and:(AudioQueueBufferRef)audioQueueBufferRef {
    
    for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
        if (audioQueueBufferRef == audioQueueBuffers[i]) {
            audioQueueBufferUsed[i] = NO;
        }
    }
}

- (void)didBecomeActive {
    
    NSLog(@"[PIAudioPlay] didBecomeActive");
    if (audioQueue != nil) {
        AudioQueueReset(audioQueue);
    }
    self.isPause = NO;
}

- (void)willResignActive {
    
    NSLog(@"[PIAudioPlay] willResignActive");
    self.isPause = YES;
}

- (void)didEnterBackground {
    
    NSLog(@"[PIAudioPlay] didEnterBackground");
    self.isPause = YES;
}

- (void)willEnterForeground {
    
    NSLog(@"[PIAudioPlay] willEnterForeground");
    if (audioQueue != nil) {
        AudioQueueReset(audioQueue);
    }
    self.isPause = NO;
}

- (void)addObservers {
    
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    [notificationCenter addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
    [notificationCenter addObserver:self selector:@selector(willEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
    [notificationCenter addObserver:self selector:@selector(didBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
    [notificationCenter addObserver:self selector:@selector(willResignActive) name:UIApplicationWillResignActiveNotification object:nil];
}

- (void)dealloc {

    NSLog(@"****[PIAudioPlay] dealloc****");
    [self stopAudioPlay];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end
